#include <crt_util.h>

#if (TARGET_OS == OS_WIN)

int _sem_init(_sem_t* sem, unsigned int value)
{
    rc_error(sem != NULL, S_ERROR);

    *sem = CreateSemaphore(NULL, value, INT_MAX, NULL);

    if (*sem == NULL)
        return S_ERROR;
    else
        return S_SUCCESS;

    return S_SUCCESS;
}

int _sem_destroy(_sem_t* sem)
{
    rc_error(sem != NULL, S_ERROR);

    if (*sem != INVALID_HANDLE_VALUE)
        CloseHandle(*sem);

    return S_SUCCESS;
}

int _sem_post(_sem_t* sem)
{
    rc_error(sem != NULL, S_ERROR);

    if (ReleaseSemaphore(*sem, 1, NULL) == FALSE)
        return S_ERROR;
    return S_SUCCESS;
}

int _sem_wait(_sem_t* sem)
{
    rc_error(sem != NULL, S_ERROR);
    DWORD dw = WaitForSingleObject(*sem, INFINITE);

    if (dw == WAIT_OBJECT_0)
        return S_SUCCESS;

    if (dw == WAIT_TIMEOUT)
        return S_ERROR;

    return S_SUCCESS;
}

int _sem_trywait(_sem_t* sem)
{
    rc_error(sem != NULL, S_ERROR);

    DWORD r = WaitForSingleObject(*sem, 0);

    if (r == WAIT_OBJECT_0)
        return S_SUCCESS;

    if (r == WAIT_TIMEOUT)
        return S_ERROR;

    return S_ERROR;
}

int _sem_wait_time(_sem_t* sem, unsigned int sec)
{
    rc_error(sem != NULL, S_ERROR);

    DWORD r = WaitForSingleObject(*sem, sec * 1000);

    if (r == WAIT_OBJECT_0)
        return S_SUCCESS;

    if (r == WAIT_TIMEOUT)
        return S_ERROR;

    return S_ERROR;
}

#elif (TARGET_OS == OS_POSIX)

int _sem_init(_sem_t* sem, unsigned int value)
{
    int r = 0;

    rc_error(sem != NULL, S_ERROR);

    r = sem_init(sem, 0, value);

    if (r == 0) {
    } else if (r == -1 && errno == EINTR) {
        return S_ERROR;
    }

    return S_SUCCESS;
}

int _sem_destroy(_sem_t* sem)
{
    int r = 0;

    rc_error(sem != NULL, S_ERROR);

    r = sem_destroy(sem);

    if (r == 0) {
    } else if (r == -1 && errno == EINTR) {
        return S_ERROR;
    }

    return S_SUCCESS;
}

int _sem_post(_sem_t* sem)
{
    int r = 0;

    rc_error(sem != NULL, S_ERROR);

    r = sem_post(sem);

    if (r == 0) {
    } else if (r == -1 && errno == EINTR) {
        return S_ERROR;
    }

    return S_SUCCESS;
}

int _sem_wait(_sem_t* sem)
{
    int r = 0;

    rc_error(sem != NULL, S_ERROR);

    r = sem_wait(sem);

    if (r == 0) {
    } else if (r == -1 && errno == EINTR) {
        return S_ERROR;
    }

    return S_SUCCESS;
}

int _sem_trywait(_sem_t* sem)
{
    int r = 0;

    rc_error(sem != NULL, S_ERROR);

    r = sem_trywait(sem);

    if (r == 0) {
        return S_SUCCESS;
    } else if (r == -1 && errno == EINTR) {
        return S_ERROR;
    }
    return S_SUCCESS;
}
int _sem_wait_time(_sem_t* sem, unsigned int sec)
{
    int r = 0;

#ifdef HAVE_SYS_SEM_WAITTIME

    struct timespec expire = {0, 0};
    rc_error(sem != NULL, S_ERROR);
#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)

    expire.tv_sec = sec;
    r = sem_timedwait(sem, &expire);

    if (r == 0) {
    } else if (r == -1 && errno == EINTR) {
        return S_ERROR;
    }
    return S_SUCCESS;
#else

#endif
#else

#endif // HAVE_SYS_SEM_WAITTIME

    return S_SUCCESS;
}

#elif (TARGET_OS == OS_MACH)

int _sem_init(_sem_t* sem, unsigned int value)
{
    kern_return_t err;

    rc_error(sem != NULL, S_ERROR);
    err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value);
    rc_error(err == KERN_SUCCESS, S_ERROR);
    return S_SUCCESS;
}

int _sem_destroy(_sem_t* sem)
{
    kern_return_t err;
    rc_error(sem != NULL, S_ERROR);
    err = semaphore_destroy(mach_task_self(), *sem);
    rc_error(err == KERN_SUCCESS, S_ERROR);
    return S_SUCCESS;
}

int _sem_post(_sem_t* sem)
{
    kern_return_t err;
    rc_error(sem != NULL, S_ERROR);
    err = semaphore_signal(*sem);
    rc_error(err == KERN_SUCCESS, S_ERROR);
    return S_SUCCESS;
}

int _sem_wait(_sem_t* sem)
{
    kern_return_t err;
    rc_error(sem != NULL, S_ERROR);
    err = semaphore_wait(*sem);
    rc_error(err == KERN_SUCCESS, S_ERROR);
    return S_SUCCESS;
}

int _sem_trywait(_sem_t* sem)
{
    rc_error(sem != NULL, S_ERROR);
    mach_timespec_t interval;
    kern_return_t err;
    interval.tv_sec = 0;
    interval.tv_nsec = 0;
    err = semaphore_timedwait(*sem, interval);
    rc_error(err == KERN_SUCCESS, S_ERROR);
    return S_SUCCESS;
}

int _sem_wait_time(_sem_t* sem, unsigned int sec)
{
    return S_ERROR;
}

#elif (TARGET_OS == OS_UNIX)

int _sem_init(_sem_t* sem, unsigned int value)
{
    int r = 0;
    rc_error(sem != NULL, S_ERROR);

    return S_SUCCESS;
}

int _sem_destroy(_sem_t* sem)
{
    int r = 0;
    rc_error(sem != NULL, S_ERROR);

    return S_SUCCESS;
}

int _sem_post(_sem_t* sem)
{
    int r = 0;
    rc_error(sem != NULL, S_ERROR);
    return S_SUCCESS;
}
int _sem_wait(_sem_t* sem)
{
    int r = 0;
    rc_error(sem != NULL, S_ERROR);

    return S_SUCCESS;
}

int _sem_trywait(_sem_t* sem)
{
    int r = 0;

    rc_error(sem != NULL, S_ERROR);

    return S_SUCCESS;
}
int _sem_wait_time(_sem_t* sem, unsigned int sec)
{
    int r = 0;
    rc_error(sem != NULL, S_ERROR);

    return S_SUCCESS;
}

#endif
